
/**	
*	
	Doyle spirals : leys Papar
*/

import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Polygon;
import java.awt.event.ActionListener;
import java.io.PrintStream;


import java.util.Hashtable;
import java.util.Locale;
import java.util.Random; 

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;




import pluginUtils.Circle;
import pluginUtils.Complexe;



public class LeysDoyle extends ImagePlus implements PlugIn,ChangeListener{
	{Locale.setDefault(Locale.US); }
	
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	
	private Random generator=new Random(); // One always needs some randomness
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private Color[] palette1={Color.YELLOW,Color.RED,Color.BLUE,Color.GREEN,Color.MAGENTA,Color.ORANGE,Color.CYAN};
	
	private double rcp(double angle){
		double auxi= (r*r+Math.cos(angle)); 
		return auxi*auxi; 
	}
	
	private double rac(double angle){
		double prod=-rcp(angle)/(r*r-1); 
		return Math.sqrt(prod*prod-1); 
	}
	// complicated function to approximate phi
	private double fonction(double angle){
		double sum=2*rcp(angle)/(r*r-1);
		sum+=2*rcp(angle)*rac(angle);
		sum+=(r*r-1)*(1-f*f); 
		sum-=2*f*rcp(angle)/1 ;// TODO attention aux signes !!!
		return 0; 
	}
	
	
	// Settings
	private double f; 
	
	private double r=0.0;
	private double rmin=0.0; 
	private double rmax=1.0; 
	
	
	
	private double theta=0;   
	private double thetamin=0; 
	private double thetamax=2*Math.PI;
	
	private double phi=0;   
	private double phimin=0; 
	private double phimax=2*Math.PI;
	
	private JSlider slider= new JSlider(JSlider.HORIZONTAL,0, 100, 0);
	private JSlider slidetheta= new JSlider(JSlider.HORIZONTAL,0, 100, 0);
	private JSlider slidephi= new JSlider(JSlider.HORIZONTAL,0, 100, 0);
	
	private JLabel rValue=new JLabel("Radius : "+r); 
	private JLabel thetaValue=new JLabel("theta : "+theta);
	private JLabel phiValue=new JLabel("phi : "+phi);
	
	// end Settings
	
	{
	JFrame frame = new JFrame("Settings");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setPreferredSize(new Dimension(800,200)); 
    GridLayout layout = new GridLayout(6,3);
    frame.getContentPane().setLayout(layout); 
    
    //	      Setting R
    
    frame.getContentPane().add(rValue);
  
    
    Hashtable<Integer,JLabel> labelTableR = new Hashtable<Integer,JLabel>();
    labelTableR.put( new Integer( 0 ), new JLabel(rmin+"") );
    labelTableR.put( new Integer( 100 ), new JLabel(rmax+"") );
    slider.setLabelTable( labelTableR );
    slider.setPaintLabels(true);
    frame.getContentPane().add(slider);
    slider.addChangeListener(this);
    
    
    
    frame.getContentPane().add(thetaValue);
    
     
    Hashtable<Integer,JLabel> labelTabletheta = new Hashtable<Integer,JLabel>();
    labelTabletheta.put( new Integer( 0 ), new JLabel(thetamin+"") );
    labelTabletheta.put( new Integer( 100 ), new JLabel(thetamax+"") );
    slidetheta.setLabelTable( labelTabletheta );
    slidetheta.setPaintLabels(true);
    frame.getContentPane().add(slidetheta);
    slidetheta.addChangeListener(this);
   
    frame.getContentPane().add(phiValue);
    
    
    Hashtable<Integer,JLabel> labelTablephi = new Hashtable<Integer,JLabel>();
    labelTablephi.put( new Integer( 0 ), new JLabel(phimin+"") );
    labelTablephi.put( new Integer( 100 ), new JLabel(phimax+"") );
    slidephi.setLabelTable( labelTabletheta );
    slidephi.setPaintLabels(true);
    frame.getContentPane().add(slidephi);
    slidephi.addChangeListener(this);
    
    //Display the window.
    frame.pack();
    frame.setVisible(true);
}

	
	
	
	
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	private PrintStream output;
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	
	
	
	private void drawCircle(Circle c,int q){
		//if(intersect(c))return; 
		
		//if(c.getRadius()<0.1) return; 
		int intX=(int)(c.getCenter().getReal()*acoef+ccoef);
		int intY=(int)(c.getCenter().getIm()*vcoef+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		int rank=q%6; 		
		primeIP.setColor(palette1[q%palette1.length]);
		primeIP.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);	
			
			/*
			String s="sphere{<"+c.getCenter().getReal()+",0,"+c.getCenter().getIm()+">,";
			s+=c.getRadius(); 
			s+=" texture{Texture"+rank+"} finish{Finish"+rank+"}}"; 
			System.out.println(s);
			output.println(s);
			*/	
		}
	

	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	public void stateChanged(ChangeEvent e) {
	    JSlider source = (JSlider)e.getSource();
	    if (!source.getValueIsAdjusting()) {
	        int fps = (int)source.getValue();
	        if(source==slider){
	        r=rmin+(rmax-rmin)*(fps+0.0)/100.0;
	        rValue.setText("R :"+r);
	        }
	       
	        if(source==slidetheta){
		        theta=thetamin+(thetamax-thetamin)*(fps+0.0)/100.0;
		        thetaValue.setText("theta :"+theta); 
		        }
	        
	        if(source==slidephi){
		        phi=phimin+(phimax-phimin)*(fps+0.0)/100.0;
		        phiValue.setText("phi :"+phi); 
		        }
	        this.run(null); 
	    }
	}
	

	public void run(String arg){	
		primeIP = new ColorProcessor(X,Y);
		
		double limit=10; 
		setBounds(-limit,limit,-limit,limit); 
		setCoefs(); 

		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(5); 
		
		drawLine(0,y0,0,y1); 
		drawLine(x0,0,x1,0);
		
	
		setProcessor("Verifying", primeIP);
		/************************ The drawing begins here *******************************/
	Circle origine=new Circle(Complexe.createComplexeFromPolar(1,0),r);
	drawCircle(origine, 1);
	
	double fact1=(r*r+Math.cos(theta))/(r*r-1); 
	double deltaprime=fact1*fact1-1; 
	f=-fact1+Math.sqrt(deltaprime); 
	Complexe A=Complexe.createComplexeFromPolar(f, theta);
	Complexe VA=Complexe.UN; 
	
	
	double fact2=(r*r+Math.cos(phi))/(r*r-1); 
	double deltaprime2=fact2*fact2-1; 
	double g=-fact2+Math.sqrt(deltaprime2);
	Complexe B=Complexe.createComplexeFromPolar(g,phi); 
	Complexe VB=Complexe.UN; 
	
	
	
	Complexe AsurB=Complexe.div(A,B); 
	Complexe VAsurB=Complexe.UN; 
	
	
	Complexe BsurA=Complexe.div(B,A); 
	Complexe VBsurA=Complexe.UN; 
	
	
	Complexe UNSurA=Complexe.div(Complexe.UN,A); 
	Complexe VUNsurA=Complexe.UN; 
	
	
	Complexe UNSurB=Complexe.div(Complexe.UN,B); 
	Complexe VUNsurB=Complexe.UN; 
	
	
	for(int i=0;i<1000;i++){
		VA=Complexe.mul(VA,A); 
		Circle cA=new Circle(VA,r*Math.sqrt(VA.squareNorm())); 
		drawCircle(cA, 2); 
		VB=Complexe.mul(VB, B); 
		Circle cB=new Circle(VB,r*Math.sqrt(VB.squareNorm())); 
		drawCircle(cB, 3);
		VAsurB=Complexe.mul(VAsurB, AsurB); 
		Circle cAsurB=new Circle(VAsurB,r*Math.sqrt(VAsurB.squareNorm())); 
		drawCircle(cAsurB, 4);
		VBsurA=Complexe.mul(VBsurA,BsurA); 
		Circle cBsurA=new Circle(VBsurA,r*Math.sqrt(VBsurA.squareNorm())); 
		drawCircle(cBsurA, 5);
		VUNsurA=Complexe.mul(VUNsurA, UNSurA); 
		Circle cUNsurA=new Circle(VUNsurA,r*Math.sqrt(VUNsurA.squareNorm())); 
		drawCircle(cUNsurA, 6);
		VUNsurB=Complexe.mul(VUNsurB,UNSurB); 
		Circle cUNsurB=new Circle(VUNsurB,r*Math.sqrt(VUNsurB.squareNorm())); 
		drawCircle(cUNsurB, 0);
	}
	
	show(); 
		
	}
}
	
	
	